// All GPIOS are pull-up

#include <linux/types.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/input/matrix_keypad.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>

struct matrix_keypad_polled {
	const struct matrix_keypad_platform_data *pdata;
	struct input_dev *input;
	unsigned int row_shift;

	int out_value;
	int row_values[MATRIX_MAX_ROWS];
	int col_values[MATRIX_MAX_COLS];
	int poll_interval;
};

enum matrix_keypad_edge {
	MATRIX_KEY_GPIO_FALLING,
	MATRIX_KEY_GPIO_RISING,
};

static void keypad_all_row_input(struct matrix_keypad_polled *keypad)
{
	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
	int i;

	for (i = 0; i < pdata->num_row_gpios; i++)
		gpio_direction_input(pdata->row_gpios[i]);
}

static void keypad_all_row_output(struct matrix_keypad_polled *keypad)
{
	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
	int i;

	for (i = 0; i < pdata->num_row_gpios; i++)
		gpio_direction_output(pdata->row_gpios[i], keypad->out_value);
}

static void keypad_all_col_input(struct matrix_keypad_polled *keypad)
{
	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
	int i;

	for (i = 0; i < pdata->num_col_gpios; i++)
		gpio_direction_input(pdata->col_gpios[i]);
}

static void keypad_all_col_output(struct matrix_keypad_polled *keypad)
{
	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
	int i;

	for (i = 0; i < pdata->num_col_gpios; i++)
		gpio_direction_output(pdata->col_gpios[i], keypad->out_value);
}

static int keypad_which_row_bounce(struct matrix_keypad_polled* keypad,
		enum matrix_keypad_edge* edge)
{
	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
	int val, i;

	for (i = 0; i < pdata->num_row_gpios; i++)
	{
		val = gpio_get_value_cansleep(pdata->row_gpios[i]);
		if (val != keypad->row_values[i])
		{
			keypad->row_values[i] = val;
			*edge = (val == 0) ?
				MATRIX_KEY_GPIO_FALLING : MATRIX_KEY_GPIO_RISING;
			return i;
		}
	}

	return -1;
}

static int keypad_which_col_bounce(struct matrix_keypad_polled* keypad)
{
	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
	int val, i;

	for (i = 0; i < pdata->num_col_gpios; i++)
	{
		val = gpio_get_value_cansleep(pdata->col_gpios[i]);
		if (val != keypad->col_values[i])
		{
			keypad->col_values[i] = val;
			return i;
		}
	}

	return -1;
}

static int keypad_assert_row(struct matrix_keypad_polled* keypad,
		enum matrix_keypad_edge* edge)
{
	keypad_all_col_output(keypad);
	keypad_all_row_input(keypad);

	return keypad_which_row_bounce(keypad, edge);
}

static int keypad_assert_col(struct matrix_keypad_polled* keypad)
{
	keypad_all_row_output(keypad);
	keypad_all_col_input(keypad);

	return keypad_which_col_bounce(keypad);
}

static void matrix_keypad_polled(struct input_dev *input)
{
	struct matrix_keypad_polled *keypad = input_get_drvdata(input);
	const unsigned short *keycodes = input->keycode;
	enum matrix_keypad_edge edge;
	int row, col, code;

	row = keypad_assert_row(keypad, &edge);
	if (row >= 0)
	{
		col = keypad_assert_col(keypad);

		//pr_info("(%d,%d) = %d\n",
		//		row, col, edge);

		code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
		input_event(input, EV_MSC, MSC_SCAN, code);
		input_report_key(input, keycodes[code], !edge);
	}

	input_sync(input);
}

static int matrix_keypad_polled_start(struct input_dev *dev)
{
	return 0;
}

static void matrix_keypad_polled_stop(struct input_dev *dev)
{
}

static int matrix_keypad_polled_init_gpio(struct platform_device *pdev,
				   struct matrix_keypad_polled *keypad)
{
	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
	int i, err;

	/* initialized strobe lines as outputs, activated */
	for (i = 0; i < pdata->num_col_gpios; i++) {
		err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col");
		if (err) {
			dev_err(&pdev->dev,
				"failed to request GPIO%d for COL%d\n",
				pdata->col_gpios[i], i);
			goto err_free_cols;
		}

		gpio_direction_output(pdata->col_gpios[i], 0);
		keypad->col_values[i] = ! keypad->out_value;
	}

	for (i = 0; i < pdata->num_row_gpios; i++) {
		err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row");
		if (err) {
			dev_err(&pdev->dev,
				"failed to request GPIO%d for ROW%d\n",
				pdata->row_gpios[i], i);
			goto err_free_rows;
		}

		gpio_direction_input(pdata->row_gpios[i]);
		keypad->row_values[i] = ! keypad->out_value;
	}

	return 0;

err_free_rows:
	while (--i >= 0)
		gpio_free(pdata->row_gpios[i]);
	i = pdata->num_col_gpios;
err_free_cols:
	while (--i >= 0)
		gpio_free(pdata->col_gpios[i]);

	return err;
}

static void keypad_free_gpio(struct matrix_keypad_polled *keypad)
{
	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
	int i;

	for (i = 0; i < pdata->num_row_gpios; i++)
		gpio_free(pdata->row_gpios[i]);

	for (i = 0; i < pdata->num_col_gpios; i++)
		gpio_free(pdata->col_gpios[i]);
}

#ifdef CONFIG_OF
static struct matrix_keypad_platform_data *
matrix_keypad_polled_parse_dt(struct device *dev)
{
	struct matrix_keypad_platform_data *pdata;
	struct device_node *np = dev->of_node;
	unsigned int *gpios;
	int ret, i, nrow, ncol;

	if (!np) {
		dev_err(dev, "device lacks DT data\n");
		return ERR_PTR(-ENODEV);
	}

	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
	if (!pdata) {
		dev_err(dev, "could not allocate memory for platform data\n");
		return ERR_PTR(-ENOMEM);
	}

	pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios");
	pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");
	if (nrow <= 0 || ncol <= 0) {
		dev_err(dev, "number of keypad rows/columns not specified\n");
		return ERR_PTR(-EINVAL);
	}

	if (of_get_property(np, "linux,no-autorepeat", NULL))
		pdata->no_autorepeat = true;

	gpios = devm_kcalloc(dev,
			     pdata->num_row_gpios + pdata->num_col_gpios,
			     sizeof(unsigned int),
			     GFP_KERNEL);
	if (!gpios) {
		dev_err(dev, "could not allocate memory for gpios\n");
		return ERR_PTR(-ENOMEM);
	}

	for (i = 0; i < nrow; i++) {
		ret = of_get_named_gpio(np, "row-gpios", i);
		if (ret < 0)
			return ERR_PTR(ret);
		gpios[i] = ret;
	}

	for (i = 0; i < ncol; i++) {
		ret = of_get_named_gpio(np, "col-gpios", i);
		if (ret < 0)
			return ERR_PTR(ret);
		gpios[nrow + i] = ret;
	}

	pdata->row_gpios = gpios;
	pdata->col_gpios = &gpios[pdata->num_row_gpios];

	return pdata;
}
#else
static inline struct matrix_keypad_platform_data *
matrix_keypad_parse_dt(struct device *dev)
{
	dev_err(dev, "no platform data defined\n");

	return ERR_PTR(-EINVAL);
}
#endif

static int matrix_keypad_polled_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	const struct matrix_keypad_platform_data *pdata;
	struct matrix_keypad_polled *keypad;
	struct input_dev *input;
	int err;

	pdata = dev_get_platdata(dev);
	if (!pdata) {
		pdata = matrix_keypad_polled_parse_dt(dev);
		if (IS_ERR(pdata))
			return PTR_ERR(pdata);
	} else if (!pdata->keymap_data) {
		dev_err(&pdev->dev, "no keymap data defined\n");
		return -EINVAL;
	}

	keypad = kzalloc(sizeof(struct matrix_keypad_polled), GFP_KERNEL);
	input = devm_input_allocate_device(dev);
	if (!keypad || !input) {
		err = -ENOMEM;
		goto err_free_mem;
	}

	keypad->input = input;
	keypad->pdata = pdata;
	keypad->row_shift = get_count_order(pdata->num_col_gpios);
	of_property_read_u32(dev->of_node, "poll-interval",
			&keypad->poll_interval);

	input->open	= matrix_keypad_polled_start;
	input->close	= matrix_keypad_polled_stop;

	err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
					 pdata->num_row_gpios,
					 pdata->num_col_gpios,
					 NULL, input);
	if (err) {
		dev_err(&pdev->dev, "failed to build keymap\n");
		goto err_free_mem;
	}

	__set_bit(EV_KEY, input->evbit);
	if (!pdata->no_autorepeat)
		__set_bit(EV_REP, input->evbit);
	input_set_capability(input, EV_MSC, MSC_SCAN);
	input_set_drvdata(input, keypad);

	err = input_setup_polling(input, matrix_keypad_polled);
	if (err) {
		dev_err(&pdev->dev, "unable to set up polling, err=%d\n", err);
		return err;
	}

	input_set_poll_interval(input, keypad->poll_interval);

	err = matrix_keypad_polled_init_gpio(pdev, keypad);
	if (err)
		goto err_free_mem;

	err = input_register_device(input);
	if (err)
		goto err_free_gpio;

	input_sync(input);

	return 0;

err_free_gpio:
	keypad_free_gpio(keypad);
err_free_mem:
	input_free_device(input);
	kfree(keypad);
	return err;
}

static int matrix_keypad_polled_remove(struct platform_device *pdev)
{
	struct matrix_keypad_polled *keypad = platform_get_drvdata(pdev);

	keypad_free_gpio(keypad);
	input_unregister_device(keypad->input);
	kfree(keypad);

	return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id matrix_keypad_polled_dt_match[] = {
	{ .compatible = "gpio-matrix-keypad-polled" },
	{ }
};
MODULE_DEVICE_TABLE(of, matrix_keypad_polled_dt_match);
#endif

static struct platform_driver matrix_keypad_polled_driver = {
	.probe		= matrix_keypad_polled_probe,
	.remove		= matrix_keypad_polled_remove,
	.driver		= {
		.name	= "matrix-keypad-polled",
		.of_match_table = of_match_ptr(matrix_keypad_polled_dt_match),
	},
};
module_platform_driver(matrix_keypad_polled_driver);
